import Alamofire
import Moya

let NormalPageCount = 20

public enum ResponCache {
    case no
    case storage(urlStr:String)
    case filePath(path:String)
    case fileUrl(url:URL)
    
    func save(data: Data? = nil) {
        switch self {
        case .storage(let urlStr):
            guard let url = URL(string: urlStr) else {
                return
            }
            CacheManager.setObject(data, forKey: url.lastPathComponent)
            break
        case .fileUrl(let url):
            let directory = url.deletingLastPathComponent()
            
            if FileManager.default.fileExists(atPath: directory.path) == false {
                try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
            }
            if let data = data {
                try? data.write(to: url, options: .atomic)
            }
            else {
                try? FileManager.default.removeItem(at: url)
            }
            break
        case .filePath(let path):
            ResponCache.fileUrl(url: URL(fileURLWithPath: path)).save(data: data)
            break
        default :
            break
        }
        
    }
    
    var value : Data?{
        switch self {
        case .storage(let urlStr):
            guard let url = URL(string: urlStr) else {
                return nil
            }
            return CacheManager.object(ofType: Data.self, forKey: url.lastPathComponent)
        case .fileUrl(let url):
            return try? Data(contentsOf: url)
        case .filePath(let path):
            return ResponCache.fileUrl(url: URL(fileURLWithPath: path)).value
        default :
            return nil
        }
    }
}

public protocol RequestProtocol {
    
    var host: String { get }
    var path: String { get }
    var retryCount: Int { get }
    /// 发送 json格式 default is false
    var jsonRequest: Bool {get}
    var formData: Bool {get}
    /// 请求类型 default is get
    var method: HTTPMethod {get}
    var headers : [String:String]? {get}
    /// 缓存请求
    var cache: ResponCache { get }
    /// 参数
    var params: [String:Any]? {get}
    var optional_params: [String : Any?]? { get }
    var hideToast: Bool { get }
    
    var uploadFiles:[(name:String?,data:Data,mimeType:String)]? { get }
    
    /// Represents an HTTP task.( Moya Task)
    var task: Task {get}
    
    func generateRequest() -> (requestURL:String,params:[String:Any])
    
    var pubicCommonHeaders : [String:String]? {get}
    var pubicCommonParams : [String:String]? {get}
    
    var ignoreLog : Bool? {get}
}

extension RequestProtocol {

    // need config host
    public var jsonRequest: Bool { false }
    public var retryCount: Int { 0 }
    
    public var method : HTTPMethod { jsonRequest ? .post : .get }
    public var headers : [String:String]? {
        return  formData || uploadFiles != nil ? ["Content-Type": "multipart/form-data"] : nil
    }
    public var params: [String:Any]? {
        return optional_params?.compactMapValues { $0 }
    }
    public var optional_params: [String : Any?]? { nil }
    public var encoding: ParameterEncoding { jsonRequest ? JSONEncoding.default : URLEncoding.default}
    
    public var cache: ResponCache { .no }
    public var hideToast: Bool { true }
    public var formData: Bool { false }
    
    public var ignoreLog : Bool? { false }    
    public var uploadFiles:[(name:String?,data:Data,mimeType:String)]? { nil }
        
    public var task: Task {
        
        if let uploadFiles = uploadFiles {
            var formDataAry = [Moya.MultipartFormData]()
            for item in uploadFiles {
                let formData = Moya.MultipartFormData(provider: .data(item.data), name: "file", fileName: item.name, mimeType: item.mimeType)
                formDataAry.append(formData)
            }
            return .uploadCompositeMultipart(formDataAry , urlParameters: generateRequest().params)
        }
        
        if formData == true {
            let formDataAry = generateMultipartFormData(from: generateRequest().params)
            return .uploadMultipart(formDataAry)
        }
        
        return .requestParameters(parameters: generateRequest().params, encoding: encoding)
    }
    
    // 提供一个生成 form-data 的方法，子类可以直接调用
    func generateMultipartFormData(from params: [String: Any]) -> [Moya.MultipartFormData] {
        var formDataAry = [Moya.MultipartFormData]()
        
        for (key, value) in params {
            if let unwrappedValue = value as? String, let data = unwrappedValue.data(using: .utf8) {
                let formData = Moya.MultipartFormData(provider: .data(data), name: key)
                formDataAry.append(formData)
            }
        }
        
        return formDataAry
    }
    
    public func generateRequest() -> (requestURL: String, params: [String : Any]) {
        let newtRequestURL = host + path
        var newParams: [String: Any] = [:]
        newParams.merge(params)
        newParams.merge(pubicCommonParams)
        return (requestURL: newtRequestURL,params: newParams)
    }
    
    public var cacheKeylastPathComponent: String {
        return URL(string: host + path)?.lastPathComponent ?? (host + path)
    }
    
    public func urlCacheKey(withParams: Bool = false) -> String{
        return withParams ? cacheKeylastPathComponent.merge(params: params) : cacheKeylastPathComponent
    }
    
    @discardableResult
    public func request(loadIngView: UIView? = nil,
                 isRefresh: Bool = false,
                 callback: responCallBack? = nil
    ) -> Cancellable? {
        return NetWorkManger.request(pare: self,loadIngView: loadIngView,isRefresh: isRefresh,progress: .none,callback: callback)
    }
}
